package cn.gs.exchanger.java;

// @author gs
import cn.gs.exchanger.ExchangerClient;
import cn.gs.exchanger.ExchangerException;
import cn.gs.serializable.serialize.SerializeController;
import cn.gs.reflect.IProxy;
import cn.gs.reflect.ProxyTool;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.cglib.proxy.MethodProxy;

public class JavaExchangerClient extends ExchangerClient {

    public JavaExchangerClient(String ip, int port, ExecutorService pool) throws UnknownHostException, IOException {
        super(ip, port, pool);
    }

    public JavaExchangerClient(String ip, int port) throws UnknownHostException, IOException {
        super(ip, port);
    }

    private class ServiceProcesser implements IService {

        Method m;
        Object o;

        ServiceProcesser(Object o, Method m) {
            this.o = o;
            this.m = m;
        }

        @Override
        public List<String> process(List<String> strArgs) throws ExchangerException {
            if (m.getParameterTypes().length != strArgs.size()) {
                String s = "ServiceError SERVICE_ARGS_LENGTH_ERR " + m.getParameterTypes().length + " != " + strArgs.size();
                //Logger.getLogger(this.getClass()).error(s);
                throw ExchangerException.Type.ARGS_ERR.create(s, null);
            }
            Object[] args = new Object[strArgs.size()];
            for (int i = 0; i < args.length; ++i) {
                Object f = SerializeController.staticToObject(strArgs.get(i), m.getParameterTypes()[i]);
                args[i] = f;
            }
            try {
                Object r = m.invoke(o, args);
                List<String> ls = new LinkedList<String>();
                ls.add(SerializeController.staticToString(r));
                return ls;
            } catch (IllegalArgumentException ex) {
                throw ExchangerException.Type.ARGS_ERR.create("Invoke_Error", ex);
            } catch (Throwable ex) {
                Logger.getLogger(JavaExchangerClient.class.getName()).log(Level.WARNING, "Executing_Error", ex);
                throw ExchangerException.Type.EXECUTE_ERR.create("Executing_Error", ex);
            }
        }
    }

    public <T> void register(T obj, Class<T> clazz, boolean unique) throws ExchangerException {
        for (Method m : clazz.getMethods()) {
            ServiceProcesser p = new ServiceProcesser(obj, m);
            String name = clazz.getCanonicalName() + "." + m.getName();
            register(name, p, unique);
        }
    }

    public <T> void register(T obj, Class<T> clazz) throws ExchangerException {
        register(obj, clazz, false);
    }

    public <T> void register(T obj, Class<T> clazz, String instanceName, boolean unique) throws ExchangerException {
        for (Method m : clazz.getMethods()) {
            if (obj != null || Modifier.isStatic(m.getModifiers())) {
                if (!Object.class.equals(m.getDeclaringClass())) {
                    ServiceProcesser p = new ServiceProcesser(obj, m);
                    String name = instanceName + "." + m.getName();
                    register(name, p, unique);
                }
            }
        }
    }

    public <T> void register(T obj, Class<T> clazz, String instanceName, String clientId) throws ExchangerException {
        for (Method m : clazz.getMethods()) {
            if (obj != null || Modifier.isStatic(m.getModifiers())) {
                if (!Object.class.equals(m.getDeclaringClass())) {
                    ServiceProcesser p = new ServiceProcesser(obj, m);
                    String name = instanceName + "." + m.getName() + "@" + clientId;
                    register(name, p, true);
                }
            }
        }
    }

    public void delete(Class<?> clazz) throws ExchangerException {
        for (Method m : clazz.getMethods()) {
            String name = clazz.getCanonicalName() + "." + m.getName();
            delete(name);
        }
    }

    private class ProxyProcesser implements IProxy {

        String prefix;
        String suffix;

        public ProxyProcesser(Class<?> clazz) {
            this.prefix = clazz.getCanonicalName();
            this.suffix = "";
        }

        public ProxyProcesser(Class<?> clazz, String instance) {
            this.prefix = instance;
            this.suffix = "";
        }

        public ProxyProcesser(Class<?> clazz, String instance, String clientId) {
            this.prefix = instance;
            this.suffix = "@" + clientId;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            List<String> r = ask(prefix + "." + method.getName() + suffix, args);
            if (r == null || r.isEmpty()) {
                return null;
            }
            Object o = SerializeController.staticToObject(r.get(0), method.getReturnType());
            return o;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] os, MethodProxy mp) throws Throwable {
            return invoke(o, method, os);
        }
    }

    public <T> T bind(Class<T> clazz, String instance, String clientId) {
        ProxyProcesser p = new ProxyProcesser(clazz, instance, clientId);
        return (T) ProxyTool.create(p, clazz);
    }

    public <T> T bind(Class<T> clazz, String instance) {
        ProxyProcesser p = new ProxyProcesser(clazz, instance);
        return (T) ProxyTool.create(p, clazz);
    }

    public <T> T bind(Class<T> clazz) {
        ProxyProcesser p = new ProxyProcesser(clazz);
        return (T) ProxyTool.create(p, clazz);
    }

    public List<String> ask(String name, Object... objs) throws ExchangerException {
        List<String> ls = new LinkedList<String>();
        if (null != objs) {
            for (Object o : objs) {
                String s = SerializeController.staticToString(o);
                ls.add(s);
            }
        }
        return dealAnswer(ask(name, ls));
    }

    public <T> T ask(String name, Class<T> clazz, Object... objs) throws ExchangerException {
        List<String> s = ask(name, objs);
        if (s == null || s.isEmpty()) {
            return null;
        }
        T t = SerializeController.staticToObject(s.get(0), clazz);
        return t;
    }
}
